package com.codi.api.controller;

import com.codi.api.security.UserUtil;
import com.codi.api.validation.AccountValidation;
import com.codi.api.validation.TradeValidation;
import com.codi.base.log.annotation.Loggable;
import com.codi.base.util.EqualsUtil;
import com.codi.base.util.StringUtil;
import com.codi.base.util.TimeUtil;
import com.codi.base.web.ResponseUtils;
import com.codi.bus.constant.GlobalConstant;
import com.codi.bus.constant.RuleConstant;
import com.codi.bus.core.domain.Lookup;
import com.codi.bus.core.domain.User;
import com.codi.bus.core.resultModel.BaseResult;
import com.codi.bus.core.resultModel.PaperInfoTestResult;
import com.codi.bus.core.resultModel.ValidateTokenResult;
import com.codi.bus.core.service.*;
import com.codi.bus.core.service.model.NewsModel;
import com.codi.bus.core.service.model.ShareTypeModel;
import com.codi.bus.core.service.model.T0Model;
import com.codi.bus.core.service.util.FundUtil;
import com.codi.bus.core.util.BaseResultUtil;
import com.codi.bus.core.vo.BuyVo;
import com.codi.bus.core.vo.SellVo;
import com.codi.bus.util.LookupUtil;
import com.codi.message.constant.ErrorConst;
import com.codi.message.constant.ErrorConstant;
import com.codi.message.util.MessageUtil;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;

@Controller
@RequestMapping("/trade")
public class TradeController {

    private static final Logger logger = LoggerFactory.getLogger(TradeController.class);

    @Resource(name = "tradeService")
    private TradeService tradeService;

    @Resource(name = "userService")
    private UserService userService;

    @Resource(name = "queryService")
    private QueryService queryService;

    @Autowired
    private PaperInfoService paperInfoService;

    @Autowired
    private TradeValidation tradeValidation;

    @Autowired
    private WalletService walletService;


    @Autowired
    private LookupService lookupService;

    @Autowired
    private AccountValidation accountValidation;

    @Autowired
    private BranchBankService branchBankService;
    @Autowired
    private T0Service t0Service;

    /**
     * 赎回转购文案
     *
     * @return
     */
    @Loggable
    @GetMapping("/sellToWalletString")
    @ResponseBody
    public BaseResult getSellToWalletString() {
        String fundReport = "R";
        String resultStr = "七日年化收益";
        BaseResult result = new BaseResult();

        boolean isOrg = false;

        Long userId = UserUtil.getLoginUserIdIfNeed();
        if (userId != null) {
            User user = userService.getUser(userId);
            if (user.getIsOrg()) {
                isOrg = true;
            }
        }
        Lookup lookup = lookupService.getLookup(RuleConstant.T0FUND_LIST_DISPLAY_FUNDS);

        List<T0Model> data;

        if (EqualsUtil.equals(1, lookup.getRuleStatus().intValue())) { // 使用
            String valueStr = lookup.getRuleValue();
            String[] fundCodeArray = valueStr.split(",");

            data = t0Service.queryT0Funds(isOrg, Arrays.asList(fundCodeArray));
        } else { // 不是用
            data = t0Service.queryT0Funds(isOrg);
        }
        List<T0Model> returnList = Lists.newArrayList();
        for (T0Model model : data) {
            if (fundReport.equals(model.getFundReport())) {
                returnList.add(model);
            }
        }

        List<String> t0Vo = new ArrayList<>();
        returnList.forEach(t0Model -> {
            t0Vo.add(t0Model.getLatestWeeklyYieldStr());
        });
        t0Vo.sort(String::compareTo);
        String number = t0Vo.get(0) + "%~" + t0Vo.get(t0Vo.size() - 1) + "%";
        Map<String,String> resultMap = new HashMap<>();
        resultMap.put("title",resultStr);
        resultMap.put("number",number);
        result.setResult(resultMap);
        return result;
    }

    @Loggable
    @RequestMapping(value = "/sell", method = RequestMethod.POST)
    public void sell(HttpServletRequest request, HttpServletResponse response, @RequestParam String token,
                     @RequestParam String bankAccount, @RequestParam String paymentType, @RequestParam String fundName,
                     @RequestParam String fundCode, @RequestParam BigDecimal shares, @RequestParam String tradePassword,
                     @RequestParam String sign, @RequestParam String applyNo,
                     @RequestHeader(value = GlobalConstant.HEADER_APP_DEVICE) String deviceId) {

        BaseResult result = null;
        // 验证token
        ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
        if (!validateTokenResult.getSuccess()) {
            String errorMessage = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
            validateTokenResult.setErrorMessage(errorMessage);
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }
        // 验证输入
        result = tradeValidation.validateSell(bankAccount, paymentType, fundName, fundCode, shares, tradePassword,
            token, applyNo, sign, validateTokenResult.getUserId());
        if (result != null) {
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        // 获取份额类别
        ShareTypeModel shareTypeModel = queryService.getShareType(fundCode);
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result = new BaseResult();
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND, null));
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }

        // 获取份额类别
        String shareType = shareTypeModel.getShareType();

        // 后台不再做验证，APP做完交易限制验证后，后台直接发给恒生，由恒生来决定是否可以做交易
        /*
         * TradeLimitModel limitModel = queryService.getTradeLimit(fundCode,
         * GlobalConstant.FUND_BUSIN_CODE_APPLY_SELL, shareType,
         * GlobalConstant.CUST_TYPE_PERSONAL, paymentType); if (limitModel !=
         * null) { //不做最低赎回限制，因为需要考虑全部赎回的情况，全部赎回有可能小于最低赎回限制 // 验证最低起购份额
         * BigDecimal minValue = limitModel.getMinValue(); if
         * (shares.compareTo(minValue) == -1) { result = new BaseResult();
         * result.setSuccess(false);
         * result.setErrorCode(ErrorConstant.ERROR_SELL_LESS_THAN_MIN_VALUE);
         * result.setErrorMessage(MessageUtil.getErrorMessage(result.
         * getErrorCode(), null)); ResponseUtils.renderJson(request, response,
         * result, true); return; } // 验证最高可赎回的份额 BigDecimal maxValue =
         * limitModel.getMaxValue(); if (shares.compareTo(maxValue) == 1) {
         * result = new BaseResult(); result.setSuccess(false);
         * result.setErrorCode(ErrorConstant.ERROR_SELL_MORE_THAN_MAX_VALUE);
         * result.setErrorMessage(MessageUtil.getErrorMessage(result.
         * getErrorCode(), null)); ResponseUtils.renderJson(request, response,
         * result, true); return; } }
         */

        // 调用接口
        result = tradeService.sell(validateTokenResult.getUserId(), token, bankAccount, paymentType, fundName, fundCode,
            shares, shareType, tradePassword, applyNo);
        if (result.getSuccess() == false && StringUtil.isEmpty(result.getErrorMessage())) {
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
        }


        ResponseUtils.renderJson(request, response, result, true);
    }

    @Loggable
    @RequestMapping(value = "/sell/v2", method = RequestMethod.POST)
    public void sellv2(HttpServletRequest request, HttpServletResponse response, @RequestParam String token,
                       @RequestParam String bankAccount, @RequestParam String paymentType, @RequestParam String fundName,
                       @RequestParam String fundCode, @RequestParam BigDecimal shares, @RequestParam String tradePassword,
                       @RequestParam String sign, @RequestParam String applyNo, @RequestParam String vcBranchbank,
                       @RequestParam String vcBankname,
                       @RequestHeader(value = GlobalConstant.HEADER_APP_DEVICE) String deviceId) {

        BaseResult result = null;
        // 验证token
        ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
        if (!validateTokenResult.getSuccess()) {
            String errorMessage = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
            validateTokenResult.setErrorMessage(errorMessage);
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }
        // 验证输入
        result = tradeValidation.validateSellV2(bankAccount, paymentType, fundName, fundCode, shares, tradePassword,
            token, applyNo, sign, validateTokenResult.getUserId(), vcBranchbank, vcBankname);
        if (result != null) {
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        //支行修改
        BaseResult resultV = null;
        resultV = accountValidation.setBranchBankValidate(vcBranchbank, tradePassword);
        if (resultV != null) {
            ResponseUtils.renderJson(request, response, resultV, true);
            return;
        }
        resultV = branchBankService.setBranchBank(validateTokenResult.getUserId(), vcBranchbank, vcBankname, tradePassword);
        if (!resultV.getSuccess()) {
            ResponseUtils.renderJson(request, response, resultV, true);
            return;
        }

        // 获取份额类别
        ShareTypeModel shareTypeModel = queryService.getShareType(fundCode);
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result = new BaseResult();
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND, null));
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }

        // 获取份额类别
        String shareType = shareTypeModel.getShareType();

        // 后台不再做验证，APP做完交易限制验证后，后台直接发给恒生，由恒生来决定是否可以做交易
        /*
         * TradeLimitModel limitModel = queryService.getTradeLimit(fundCode,
         * GlobalConstant.FUND_BUSIN_CODE_APPLY_SELL, shareType,
         * GlobalConstant.CUST_TYPE_PERSONAL, paymentType); if (limitModel !=
         * null) { //不做最低赎回限制，因为需要考虑全部赎回的情况，全部赎回有可能小于最低赎回限制 // 验证最低起购份额
         * BigDecimal minValue = limitModel.getMinValue(); if
         * (shares.compareTo(minValue) == -1) { result = new BaseResult();
         * result.setSuccess(false);
         * result.setErrorCode(ErrorConstant.ERROR_SELL_LESS_THAN_MIN_VALUE);
         * result.setErrorMessage(MessageUtil.getErrorMessage(result.
         * getErrorCode(), null)); ResponseUtils.renderJson(request, response,
         * result, true); return; } // 验证最高可赎回的份额 BigDecimal maxValue =
         * limitModel.getMaxValue(); if (shares.compareTo(maxValue) == 1) {
         * result = new BaseResult(); result.setSuccess(false);
         * result.setErrorCode(ErrorConstant.ERROR_SELL_MORE_THAN_MAX_VALUE);
         * result.setErrorMessage(MessageUtil.getErrorMessage(result.
         * getErrorCode(), null)); ResponseUtils.renderJson(request, response,
         * result, true); return; } }
         */

        // 调用接口
        result = tradeService.sell(validateTokenResult.getUserId(), token, bankAccount, paymentType, fundName, fundCode,
            shares, shareType, tradePassword, applyNo);
        if (result.getSuccess() == false && StringUtil.isEmpty(result.getErrorMessage())) {
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
        }


        ResponseUtils.renderJson(request, response, result, true);
    }

    /**
     * 赎回普通基金转购 瑞富宝
     *
     * @param deviceId
     * @return
     * @author zx
     * @date 2018-04-16 14:10:15
     */
    @Loggable
    @PostMapping("/sellForWallet")
    @ResponseBody
    public BaseResult sellForWallet(@Validated SellVo sellVo,
                                    @RequestHeader(value = GlobalConstant.HEADER_OS_TYPE) String deviceId) {
        Long userId = UserUtil.getLoginUserId();

        sellVo.setUserId(userId);
        sellVo.setChannelId(deviceId);
        sellVo.setMoneyType(GlobalConstant.MONEY_TYPE_RMB);

        BaseResult result;

        // 验证输入
//        result = tradeValidation.validateSellForWallet(sellVo);
//        if (result != null) {
//            logger.info("trade validation fail.");
//            return result;
//        }

        // 调用接口
        result = tradeService.sellForWallet(sellVo);

        BaseResultUtil.parseErrMsg(result);

        return result;
    }

    @Loggable
    @RequestMapping(value = "/buy", method = RequestMethod.POST)
    public void buy(HttpServletRequest request, HttpServletResponse response, @RequestParam String token,
                    @RequestParam String bankAccount, @RequestParam String paymentType, @RequestParam Integer moneyType,
                    @RequestParam String fundName, @RequestParam String fundCode, @RequestParam BigDecimal fundBalance,
                    @RequestParam String tradePassword, @RequestParam boolean checkRisk, @RequestParam String sign,
                    @RequestParam String applyNo, @RequestHeader(value = GlobalConstant.HEADER_APP_DEVICE) String deviceId) {

        BaseResult result = null;
        // 验证token
        ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
        if (!validateTokenResult.getSuccess()) {
            String errorMessage = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
            validateTokenResult.setErrorMessage(errorMessage);
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }
        // 验证输入
        result = tradeValidation.validateBuy(bankAccount, paymentType, moneyType, fundName, fundCode, fundBalance,
            tradePassword, token, applyNo, validateTokenResult.getUserId(), sign);
        if (result != null) {
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        // 检查基金的风险等级和用户的等级
        if (checkRisk && invalidRisk(request, response, fundCode, validateTokenResult)) {
            return;
        }

        ShareTypeModel shareTypeModel = queryService.getShareType(fundCode);
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result = new BaseResult();
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND, null));
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        // 获取份额类别
        String shareType = shareTypeModel.getShareType();

        // 后台不再做验证，APP做完交易限制验证后，后台直接发给恒生，由恒生来决定是否可以做交易
        /*
         * TradeLimitModel limitModel = queryService.getTradeLimit(fundCode,
         * GlobalConstant.FUND_BUSIN_CODE_APPLY_BUY, shareType,
         * GlobalConstant.CUST_TYPE_PERSONAL, paymentType); if (limitModel !=
         * null) { // 验证最低起购金额 BigDecimal minValue = limitModel.getMinValue();
         * if (fundBalance.compareTo(minValue) == -1) { result = new
         * BaseResult(); result.setSuccess(false);
         * result.setErrorCode(ErrorConstant.ERROR_BUY_LESS_THAN_MIN_VALUE);
         * result.setErrorMessage(MessageUtil.getErrorMessage(result.
         * getErrorCode(), null)); ResponseUtils.renderJson(request, response,
         * result, true); return; } // 验证最高可购金额 BigDecimal maxValue =
         * limitModel.getMaxValue(); if (fundBalance.compareTo(maxValue) == 1) {
         * result = new BaseResult(); result.setSuccess(false);
         * result.setErrorCode(ErrorConstant.ERROR_BUY_MORE_THAN_MAX_VALUE);
         * result.setErrorMessage(MessageUtil.getErrorMessage(result.
         * getErrorCode(), null)); ResponseUtils.renderJson(request, response,
         * result, true); return; } }
         */

        // 调用接口
        result = tradeService.buy(validateTokenResult.getUserId(), token, bankAccount, paymentType, moneyType, fundName,
            fundCode, fundBalance, shareType, tradePassword, applyNo);
        if (result.getSuccess() == false && StringUtil.isEmpty(result.getErrorMessage())) {
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
        }
        ResponseUtils.renderJson(request, response, result, true);
    }


    /**
     * 申购
     *
     * @param buyVo
     * @param deviceId
     * @return
     * @author spy
     * @date 2017-11-30 14:10:15
     */
    @Loggable
    @PostMapping("/buy/v2")
    @ResponseBody
    public BaseResult buy(@Validated BuyVo buyVo,
                          @RequestHeader(value = GlobalConstant.HEADER_OS_TYPE) String deviceId) {
        Long userId = UserUtil.getLoginUserId();

        buyVo.setUserId(userId);
        buyVo.setChannelId(deviceId);
        buyVo.setMoneyType(GlobalConstant.MONEY_TYPE_RMB);

        BaseResult result;

        // 验证输入
        result = tradeValidation.validateBuy(buyVo);
        if (result != null) {
            logger.info("trade validation fail.");
            return result;
        }

        // 调用接口
        result = tradeService.buy(buyVo);

        BaseResultUtil.parseErrMsg(result);

        return result;
    }

    @Loggable
    @RequestMapping(value = "/undo", method = RequestMethod.POST)
    public void undo(HttpServletRequest request, HttpServletResponse response, @RequestParam String token,
                     @RequestParam String originalApplyNo, @RequestParam String tradePassword, @RequestParam String sign,
                     @RequestParam String applyNo, @RequestHeader(value = GlobalConstant.HEADER_APP_DEVICE) String deviceId) {
        logger.info("undo request, originalApplyNo:" + originalApplyNo + ", applyNo:" + applyNo);
        BaseResult result = null;
        // 验证token
        ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
        if (!validateTokenResult.getSuccess()) {
            String errorMessage = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
            validateTokenResult.setErrorMessage(errorMessage);
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }
        // 验证输入
        result = tradeValidation.validateUndo(originalApplyNo, tradePassword, token, applyNo,
            validateTokenResult.getUserId(), sign);
        if (result != null) {
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        // 调用接口
        result = tradeService.undo(validateTokenResult.getUserId(), token, originalApplyNo, tradePassword);
        if (!result.getSuccess() && StringUtil.isEmpty(result.getErrorMessage())) {
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
        }
        ResponseUtils.renderJson(request, response, result, true);
    }

    /**
     * 申购定投基金
     *
     * @param request
     * @param response
     * @param token
     * @param tradePassword
     * @param sign
     * @param applyNo
     */
    @Loggable
    @RequestMapping(value = "/fix", method = RequestMethod.POST)
    public void fix(HttpServletRequest request, HttpServletResponse response, @RequestParam String token,
                    @RequestParam String tradePassword, @RequestParam boolean checkRisk, @RequestParam String applyNo,
                    @RequestParam String bankAccount, @RequestParam String paymentType, @RequestParam String fundName,
                    @RequestParam String fundCode, @RequestParam BigDecimal balance, @RequestParam String protocolPeriodUnit,
                    @RequestParam Long protocolFixDay, @RequestParam String sign,
                    @RequestHeader(value = GlobalConstant.HEADER_APP_DEVICE) String deviceId) {
        BaseResult result = null;
        // 验证token
        ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
        if (!validateTokenResult.getSuccess()) {
            String errorMessage = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
            validateTokenResult.setErrorMessage(errorMessage);
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }
        // 用户ID
        long userId = validateTokenResult.getUserId();

        // 机构用户不能定投
        User user = userService.getUser(userId);
        if (user.getIsOrg()) {
            result = new BaseResult();
            result.setSuccess(false)
                .setErrorCode(ErrorConst.COMMON_USER_PERMISSION_ONLY)
                .setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.COMMON_USER_PERMISSION_ONLY));

            ResponseUtils.renderJson(request, response, result, true);
            return;
        }


        // 验证输入
        result = tradeValidation.validateFix(bankAccount, paymentType, fundName, fundCode, tradePassword, balance,
            protocolPeriodUnit, protocolFixDay, token, applyNo, validateTokenResult.getUserId(), sign, null, null,
            null);
        if (result != null) {
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        // 检查基金的风险等级和用户的等级
        if (checkRisk && invalidRisk(request, response, fundCode, validateTokenResult)) {
            return;
        }

        // 获取份额类别
        ShareTypeModel shareTypeModel = queryService.getShareType(fundCode);
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result = new BaseResult();
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND, null));
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        String shareType = shareTypeModel.getShareType(); // 份额类别

        // 调用接口
        result = tradeService.fix(applyNo, tradePassword, protocolFixDay, shareType, bankAccount, fundName,
            protocolPeriodUnit, userId, balance, fundCode, paymentType, token, GlobalConstant.FIX_MODEL_NORMAL,
            null, null);
        if (!result.getSuccess() && StringUtil.isEmpty(result.getErrorMessage())) {
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
        }
        ResponseUtils.renderJson(request, response, result, true);
    }

    /**
     * 修改定投协议
     *
     * @param request
     * @param response
     * @param token
     * @param tradePassword
     * @param checkRisk
     * @param applyNo
     * @param bankAccount
     * @param paymentType
     * @param fundName
     * @param fundCode
     * @param balance
     * @param protocolPeriodUnit
     * @param protocolFixDay
     */
    @Loggable
    @RequestMapping(value = "/modifyfix", method = RequestMethod.POST)
    public void modifyFix(HttpServletRequest request, HttpServletResponse response, @RequestParam String token,
                          @RequestParam String tradePassword, @RequestParam boolean checkRisk,
                          @RequestParam String protocolPeriodUnit, @RequestParam String applyNo, @RequestParam String bankAccount,
                          @RequestParam String paymentType, @RequestParam String fundName, @RequestParam String fundCode,
                          @RequestParam BigDecimal balance, @RequestParam String scheduledProtocolId, @RequestParam String fixModel,
                          @RequestParam Long protocolFixDay, @RequestParam String sign, @RequestParam String expiryDate,
                          @RequestHeader(value = GlobalConstant.HEADER_APP_DEVICE) String deviceId) {
        BaseResult result = null;
        // 验证token
        ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
        if (!validateTokenResult.getSuccess()) {
            String errorMessage = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
            validateTokenResult.setErrorMessage(errorMessage);
            ResponseUtils.renderJson(request, response, validateTokenResult, true);
            return;
        }
        // 用户ID
        long userId = validateTokenResult.getUserId();

        // 机构用户不能定投
        User user = userService.getUser(userId);
        if (user.getIsOrg()) {
            result = new BaseResult();
            result.setSuccess(false)
                .setErrorCode(ErrorConst.COMMON_USER_PERMISSION_ONLY)
                .setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.COMMON_USER_PERMISSION_ONLY));

            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        // 验证输入
        result = tradeValidation.validateFix(bankAccount, paymentType, fundName, fundCode, tradePassword, balance,
            protocolPeriodUnit, protocolFixDay, token, applyNo, validateTokenResult.getUserId(), sign, expiryDate,
            fixModel, scheduledProtocolId);
        if (result != null) {
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        // 定投协议号不能为空
        if (StringUtil.isEmpty(scheduledProtocolId)) {
            result = new BaseResult();
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_FIELD_NO_FIND);
            Map<String, String> map = new HashMap<>();
            map.put(ErrorConstant.PLACEHOLDER_FIELD_NO_FIND_FIELD, "定投协议号");
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_FIELD_NO_FIND, map));
        }

        // 检查基金的风险等级和用户的等级
        if (checkRisk && invalidRisk(request, response, fundCode, validateTokenResult)) {
            return;
        }

        // 获取份额类别
        ShareTypeModel shareTypeModel = queryService.getShareType(fundCode);
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result = new BaseResult();
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND, null));
            ResponseUtils.renderJson(request, response, result, true);
            return;
        }

        String shareType = shareTypeModel.getShareType(); // 份额类别

        // 调用接口
        result = tradeService.fix(applyNo, tradePassword, protocolFixDay, shareType, bankAccount, fundName,
            protocolPeriodUnit, userId, balance, fundCode, paymentType, token, fixModel, scheduledProtocolId,
            expiryDate);
        if (!result.getSuccess() && StringUtil.isEmpty(result.getErrorMessage())) {
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
        }
        ResponseUtils.renderJson(request, response, result, true);
    }

    /**
     * 快速赎回
     *
     * @param token         用户Token
     * @param bankAccount   银行账号
     * @param paymentType   支付方式
     * @param fundName      基金名称
     * @param fundCode      基金代码
     * @param shares        份额
     * @param tradeAcco     交易账号
     * @param tradePassword 交易密码
     * @param sign          签名
     * @param applyNo       申请编号
     * @param deviceId      设备ID
     */
    @Loggable
    @RequestMapping(value = "/fastRedeem", method = RequestMethod.POST)
    @ResponseBody
    public ResponseEntity<BaseResult> fastRedeem(@RequestParam String token,
                                                 @RequestParam String bankAccount,
                                                 @RequestParam String paymentType,
                                                 @RequestParam String fundName,
                                                 @RequestParam String fundCode,
                                                 @RequestParam BigDecimal shares,
                                                 @RequestParam String tradePassword,
                                                 @RequestParam String sign,
                                                 @RequestParam String applyNo,
                                                 @RequestParam String tradeAcco,
                                                 @RequestHeader(value = GlobalConstant.HEADER_APP_DEVICE) String deviceId) {

        BaseResult result = null;
        // 验证token
        ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
        if (!validateTokenResult.getSuccess()) {
            String errorMessage = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
            validateTokenResult.setErrorMessage(errorMessage);
            return new ResponseEntity<>(validateTokenResult, HttpStatus.OK);
        }
        fundCode = fundCode.trim();

        String brandsType = FundUtil.getT0FundBrandsType(fundCode);


        long userId = validateTokenResult.getUserId();

        User user = userService.getUser(userId);
        if (user.getIsOrg()) { // 机构

            if (lookupService.isT0Fund(fundCode)) {

                // 如果是机构用户，宝盈不支持快速赎回 shi.pengyan 2017-10-11 10:24:07
                if (FundUtil.isBaoYingT0AB(fundCode)) {
                    result = new BaseResult();
                    result.setSuccess(false)
                        .setErrorCode(ErrorConst.COMMON_USER_PERMISSION_ONLY)
                        .setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.COMMON_USER_PERMISSION_ONLY));

                    return new ResponseEntity<>(result, HttpStatus.OK);
                }
                //如果是机构用户，金鹰T+0, 不支持快速赎回  zengxl 2018-04-20
                if (FundUtil.isJinYingT0AB(fundCode)) {
                    result = new BaseResult();
                    result.setSuccess(false)
                        .setErrorCode(ErrorConst.COMMON_USER_PERMISSION_ONLY)
                        .setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.COMMON_USER_PERMISSION_ONLY));

                    return new ResponseEntity<>(result, HttpStatus.OK);
                }
                // 金鹰T+0, 机构快速赎回时间为每天8:35-17:00，其余时间只能进行普通赎回
                /*if (FundUtil.isJinYingT0AB(fundCode)) {

                    String startTimeStr = lookupService.getLookup(RuleConstant.T0FUND_JINYING_START_TIME).getRuleValue();
                    String endTimeStr   = lookupService.getLookup(RuleConstant.T0FUND_JINYING_END_TIME).getRuleValue();

                    Boolean isInRange = TimeUtil.nowIsInRange(startTimeStr + ":00", endTimeStr + ":00", TimeUtil.FORMAT_HHmmSS);

                    if (!isInRange) {
                        String errorMsg = MessageUtil.getErrorMsg(ErrorConst.ORGAN_FASTREDEEM_JINYING_NOT_IN_SUPPORT_TIME
                            , startTimeStr, endTimeStr);

                        result = new BaseResult();
                        result.setSuccess(false)
                              .setErrorCode(ErrorConst.ORGAN_FASTREDEEM_JINYING_NOT_IN_SUPPORT_TIME)
                              .setErrorMessage(errorMsg);

                        return new ResponseEntity<>(result, HttpStatus.OK);
                    }
                }*/

                // 长城T+0, 机构快速取现时间为9:00-16:45，其余时间只能进行普通赎回
                if (FundUtil.isChangChengT0AB(fundCode)) {

                    String startTimeStr = lookupService.getLookup(RuleConstant.T0FUND_CHANGCHENG_START_TIME).getRuleValue();
                    String endTimeStr = lookupService.getLookup(RuleConstant.T0FUND_CHANGCHENG_END_TIME).getRuleValue();

                    Boolean isInRange = TimeUtil.nowIsInRange(startTimeStr + ":00", endTimeStr + ":00", TimeUtil.FORMAT_HHmmSS);

                    if (!isInRange) {
                        String errorMsg = MessageUtil.getErrorMsg(ErrorConst.ORGAN_FASTREDEEM_CHANGCHENG_NOT_IN_SUPPORT_TIME
                            , startTimeStr, endTimeStr);

                        result = new BaseResult();
                        result.setSuccess(false)
                            .setErrorCode(ErrorConst.ORGAN_FASTREDEEM_CHANGCHENG_NOT_IN_SUPPORT_TIME)
                            .setErrorMessage(errorMsg);

                        return new ResponseEntity<>(result, HttpStatus.OK);
                    }
                }


            }
        } else { // 个人

        }
        boolean hasDayDealLimit = false, hasDayDealCountLimit = false;
        //宝盈T+0：
        // 个人用户快赎：单笔限额5万，单日上限200万，次数不限；
        // 机构不支持快赎；

        // 金鹰T+0 长城t+0
        // a) 个人用户快赎：单笔限额200万，单日上限200万，单日次数限制10次
        // b) 机构用户快赎：单笔限额1000万，单日上限5000万，单日次数限制5次
        if (FundUtil.isBaoYingT0AB(fundCode) || FundUtil.isJinYingT0AB(fundCode) ||
            FundUtil.isChangChengT0AB(fundCode)) {

            String str;
            Lookup lookup;

            /*if (FundUtil.isBaoYingT0AB(fundCode)) {
                lookup = lookupService.getLookup(RuleConstant.T0FUND_BAOYING_CONFIG);
            } else {
                lookup = lookupService.getLookup(RuleConstant.RULE_LOOKUP_TYPE_T0FUND, fundCode);
            }*/

            lookup = lookupService.getLookup(RuleConstant.RULE_LOOKUP_TYPE_T0FUND, fundCode);

            str = lookup.getComponentValue();

            //TODO 单笔限额
            String maxSingleDealStr = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(),
                RuleConstant.FIELD_SINGLE_DEAL_LIMIT);
            if (StringUtil.isNotEmpty(maxSingleDealStr)) {
                if ("-1".equals(maxSingleDealStr)) { // 不做校验
                } else {
                    if (shares.compareTo(new BigDecimal(maxSingleDealStr)) > 1) {
                        String errorMsg = MessageUtil.getErrorMsg(ErrorConst.WALLET_HAS_REACH_MAX_SINGLE_DEAL
                            , maxSingleDealStr);

                        result = new BaseResult();
                        result.setSuccess(false)
                            .setErrorCode(ErrorConst.WALLET_HAS_REACH_MAX_SINGLE_DEAL)
                            .setErrorMessage(errorMsg);

                        return new ResponseEntity<>(result, HttpStatus.OK);
                    }
                }
            }

            //TODO 单日上限
            String maxDayDealStr = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(), RuleConstant.FIELD_DAY_LIMIT);
            if (StringUtil.isNotEmpty(maxDayDealStr)) {
                if ("-1".equals(maxDayDealStr)) {// 不做校验
                } else {
                    hasDayDealLimit = true;

                    BigDecimal currentDayDeal = walletService.getFastRedeemDayUsedMoney(userId, brandsType);
                    BigDecimal finalDeal = currentDayDeal.add(shares);
                    if (finalDeal.compareTo(new BigDecimal(maxDayDealStr)) > 1) {
                        String errorMsg = MessageUtil.getErrorMsg(ErrorConst.WALLET_HAS_REACH_MAX_DAY_DEAL
                            , maxDayDealStr);

                        result = new BaseResult();
                        result.setSuccess(false)
                            .setErrorCode(ErrorConst.WALLET_HAS_REACH_MAX_DAY_DEAL)
                            .setErrorMessage(errorMsg);

                        return new ResponseEntity<>(result, HttpStatus.OK);
                    }

                }
            }

            //TODO 单日交易限制次数
            String maxDayDealCountStr = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(),
                RuleConstant.FIELD_DAY_DEAL_COUNT_LIMIT);

            if (StringUtil.isNotEmpty(maxDayDealCountStr)) {
                if ("-1".equals(maxDayDealCountStr)) { // 不做校验
                } else {
                    hasDayDealCountLimit = true;
                    int currentCount = walletService.getFastRedeemCountInCurrentDay(userId, brandsType);
                    if (currentCount >= Integer.valueOf(maxDayDealCountStr)) {
                        String errorMsg = MessageUtil.getErrorMsg(ErrorConst.WALLET_HAS_REACH_MAX_REDEEM_TIME);

                        result = new BaseResult();
                        result.setSuccess(false)
                            .setErrorCode(ErrorConst.WALLET_HAS_REACH_MAX_REDEEM_TIME)
                            .setErrorMessage(errorMsg);

                        return new ResponseEntity<>(result, HttpStatus.OK);
                    }
                }
            }

        }

        // 验证输入
        result = tradeValidation.validateFastRedeem(bankAccount, paymentType, fundName, fundCode, shares, tradePassword,
            token, applyNo, sign, validateTokenResult.getUserId(), tradeAcco);
        if (result != null) {
            return new ResponseEntity<>(result, HttpStatus.OK);
        }

        // 获取份额类别
        ShareTypeModel shareTypeModel = queryService.getShareType(fundCode);
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result = new BaseResult();
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND, null));
            return new ResponseEntity<>(result, HttpStatus.OK);
        }

        // 获取份额类别
        String shareType = shareTypeModel.getShareType();

        // 调用接口
        result = tradeService.fastRedeem(validateTokenResult.getUserId(), token, paymentType, tradePassword, fundCode,
            shareType, shares, bankAccount, fundName, applyNo, tradeAcco);

        // 失败
        if (!result.getSuccess()) {
            if (StringUtil.isEmpty(result.getErrorMessage())) {
                result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
            }
        } else {
            if (FundUtil.isJinYingT0AB(fundCode) || FundUtil.isChangChengT0AB(fundCode)) {
                if (hasDayDealCountLimit) { // 增加交易次数
                    walletService.increaseFastRedeemCountInCurrentDay(userId, brandsType);
                }
                if (hasDayDealLimit) { // 增加交易金额
                    walletService.increaseFastRedeemDayUsedMoney(userId, brandsType, shares);
                }
            }
        }

        return new ResponseEntity<>(result, HttpStatus.OK);
    }


    /**
     * 检查风险等级是否和用户的匹配
     *对风险评测进行了修改
     * 不知道机构是否调用
     * @param request
     * @param response
     * @param fundCode
     * @param validateTokenResult
     * @return
     */

    private boolean invalidRisk(HttpServletRequest request, HttpServletResponse response, String fundCode,
                                ValidateTokenResult validateTokenResult) {
        BaseResult result;
        NewsModel fundRiskLevel = queryService.getRiskLevel(fundCode);
        PaperInfoTestResult testResult = paperInfoService.getUserPaperInfoTestResult(validateTokenResult.getUserId(),
            "",null);
        Integer investRiskLevel = 0;
        if (!StringUtil.isEmpty(testResult.getInvest_risk_tolerance())) {
            investRiskLevel = Integer.parseInt(testResult.getInvest_risk_tolerance());
        }

        // 基金的风险等级大于用户的投资风险等级
        /*进取型5,成长型4,平衡型3,稳健型2,保守型1
          高风险5,中高风险4,中风险3,中低风险2,低风险1*/
        if (investRiskLevel < fundRiskLevel.getRiskLevel()) {
            result = new BaseResult();
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_BUSINESS);
            result.setErrorCode(ErrorConstant.ERROR_RISK_LEVEL);
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
            ResponseUtils.renderJson(request, response, result, true);
            return true;
        }
        return false;
    }
}
